package com.niiwoo.civet.user.service.local.dataflow;

import com.alibaba.fastjson.JSON;
import com.niiwoo.civet.user.constants.UserRabbitConstant;
import com.niiwoo.civet.user.dao.entity.DataBackflowRetryFailRecord;
import com.niiwoo.civet.user.dao.entity.DataBackflowRetryTask;
import com.niiwoo.civet.user.dao.mapper.DataBackflowRetryFailRecordMapperExt;
import com.niiwoo.civet.user.dao.mapper.DataBackflowRetryTaskMapperExt;
import com.niiwoo.civet.user.dto.request.DataBackFlowRetryMQDTO;
import com.niiwoo.civet.user.enums.DataBackFlowRetryStatusEnum;
import com.niiwoo.civet.user.enums.DataBackFlowRetryTypeEnum;
import com.niiwoo.tripod.service.component.SnowflakeIdWorker;
import com.niiwoo.tripod.tiancheng.response.common.TianchengUapResponse;
import lombok.extern.slf4j.Slf4j;
import org.springframework.amqp.rabbit.core.RabbitTemplate;
import org.springframework.aop.framework.AopContext;
import org.springframework.beans.BeanUtils;
import org.springframework.beans.BeansException;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.context.ApplicationContext;
import org.springframework.context.ApplicationContextAware;
import org.springframework.stereotype.Service;
import org.springframework.transaction.annotation.Transactional;
import org.springframework.util.CollectionUtils;

import java.lang.reflect.Method;
import java.util.Date;
import java.util.List;
import java.util.concurrent.CompletableFuture;
import java.util.concurrent.TimeUnit;

/**
 * @Author: wuke
 * @Description:
 * @Date: Created in 16:59 2018/1/10
 */
@Slf4j
@Service
public class DataBackFlowRetryService implements ApplicationContextAware {
    private ApplicationContext applicationContext;
    @Autowired
    private DataBackflowRetryTaskMapperExt dataBackflowRetryTaskMapper;
    @Autowired
    private DataBackflowRetryFailRecordMapperExt dataBackflowRetryFailRecordMapper;
    @Autowired
    private SnowflakeIdWorker snowflakeIdWorker;
    @Autowired
    private RabbitTemplate rabbitTemplate;

    private static final int DATA_BACK_FLOW_RETRY_MAX_CALL_TIMES = 3; //数据回流重试最大次数
    private static final String TIANCHENG_SUCCESS="0";

    @Override
    public void setApplicationContext(ApplicationContext applicationContext) throws BeansException {
        this.applicationContext = applicationContext;
    }

    /**
     * 保存数据回流重试任务数据
     * @param dataBackflowRetryTask
     */
    public void saveDataFlowRetryTask(DataBackflowRetryTask dataBackflowRetryTask){
        Date now = new Date();
        dataBackflowRetryTask.setId(snowflakeIdWorker.nextId());
        dataBackflowRetryTask.setCreateTime(now);
        dataBackflowRetryTask.setUpdateTime(now);
        dataBackflowRetryTask.setCallTimes(0);
        dataBackflowRetryTask.setProcessStatus(DataBackFlowRetryStatusEnum.NO_PROCESS.getProcessStatus());
        dataBackflowRetryTaskMapper.insert(dataBackflowRetryTask);
    }

    public List<DataBackflowRetryTask> listRetryTask(Integer pageSize){
        List<DataBackflowRetryTask> retryTaskList = dataBackflowRetryTaskMapper.listRetryTask(pageSize);
        return retryTaskList;
    }

    /**
     * 发送数据回流重试消息
     */
    public void sendDataBackFlowRetry(){
        while (true) {
            List<DataBackflowRetryTask> retryTaskList = this.listRetryTask(100);
            log.info("sendDataBackFlowRetry.retryTaskList={}",retryTaskList);
            if (CollectionUtils.isEmpty(retryTaskList)) {
                break;
            }
            log.info("sendDataBackFlowRetry.retryTaskList.size={}",retryTaskList.size());
            dataBackflowRetryTaskMapper.batchUpdateRetryTaskProcessStatus(DataBackFlowRetryStatusEnum.IN_PROCESS,retryTaskList);
            retryTaskList.forEach(retryTask -> {
                DataBackFlowRetryMQDTO mqDTO = new DataBackFlowRetryMQDTO();
                BeanUtils.copyProperties(retryTask,mqDTO);
                log.info("sendDataBackFlowRetry.mqDTO{}",JSON.toJSONString(mqDTO));
                rabbitTemplate.convertAndSend(UserRabbitConstant.Exchange.DATA_BACKFLOW_RETRY, UserRabbitConstant.RoutingKey.DATA_BACKFLOW_RETRY, mqDTO);
            });
        }
    }

    /**
     * 推送天秤数据回流数据
     * @param dataBackFlowRetryMQDTO
     */
    public void dataBackFlowPushToTianCheng(DataBackFlowRetryMQDTO dataBackFlowRetryMQDTO){
        long time = System.currentTimeMillis();
        log.info("dataBackFlowPushToTianCheng.time={},dataBackFlowRetryMQDTO={}",time,JSON.toJSONString(dataBackFlowRetryMQDTO));
        DataBackFlowRetryTypeEnum dataBackFlowRetryTypeEnum = DataBackFlowRetryTypeEnum.getByFunctionCode(dataBackFlowRetryMQDTO.getFunctionCode());
        log.info("dataBackFlowPushToTianCheng.time={},dataBackFlowRetryTypeEnum={}",time,JSON.toJSONString(dataBackFlowRetryTypeEnum));
        Object dataFlowService = this.applicationContext.getBean(dataBackFlowRetryTypeEnum.getBackFlowServiceClass());
        log.info("dataBackFlowPushToTianCheng.time={},dataFlowService={}",time,dataFlowService);
        Class dataFlowServiceClazz = dataFlowService.getClass();

        try {
            Method method = dataFlowServiceClazz.getMethod(dataBackFlowRetryTypeEnum.getBackFlowMethodName(), dataBackFlowRetryTypeEnum.getBackFlowRequestClass());
            CompletableFuture<TianchengUapResponse> future = (CompletableFuture<TianchengUapResponse>)method.invoke(dataFlowService,JSON.parseObject(dataBackFlowRetryMQDTO.getBackflowData(),dataBackFlowRetryTypeEnum.getBackFlowRequestClass()));
            TianchengUapResponse tianchengUapResponse = future.get(5, TimeUnit.SECONDS);
            log.info("dataBackFlowPushToTianCheng.time={},tianchengUapResponse={}",time,JSON.toJSONString(tianchengUapResponse));
            getSelfProxy().dataBackFlowRetryDataUpdate(dataBackFlowRetryMQDTO,tianchengUapResponse);
        } catch (Exception e) {
            log.error("dataBackFlowPushToTianCheng error",e);
            getSelfProxy().dataBackFlowRetryExceptionDataUpdate(dataBackFlowRetryMQDTO,e);
        }
    }

    @Transactional(rollbackFor = Exception.class)
    public void dataBackFlowRetryDataUpdate(DataBackFlowRetryMQDTO dataBackFlowRetryMQDTO,TianchengUapResponse tianchengUapResponse){
        log.info("dataBackFlowRetryDataUpdate----");
        Date now = new Date();
        String tianchengUapResponseStatus = tianchengUapResponse.getStatus();
        if(dataBackFlowRetryMQDTO.getCallTimes() >= (DATA_BACK_FLOW_RETRY_MAX_CALL_TIMES-1)
                || TIANCHENG_SUCCESS.equals(tianchengUapResponseStatus)){
            dataBackflowRetryTaskMapper.deleteByPrimaryKey(dataBackFlowRetryMQDTO.getId());
        }else{
            DataBackflowRetryTask dataBackflowRetryTask = new DataBackflowRetryTask();
            dataBackflowRetryTask.setId(dataBackFlowRetryMQDTO.getId());
            dataBackflowRetryTask.setProcessStatus(DataBackFlowRetryStatusEnum.FAIL_PROCESS.getProcessStatus());
            dataBackflowRetryTask.setCallTimes(dataBackFlowRetryMQDTO.getCallTimes()+1);
            dataBackflowRetryTask.setUpdateTime(now);
            dataBackflowRetryTaskMapper.updateByPrimaryKeySelective(dataBackflowRetryTask);
        }

        if(dataBackFlowRetryMQDTO.getCallTimes() >= (DATA_BACK_FLOW_RETRY_MAX_CALL_TIMES-1)
                && !TIANCHENG_SUCCESS.equals(tianchengUapResponseStatus)){
            DataBackflowRetryFailRecord dataBackflowRetryFailRecord = new DataBackflowRetryFailRecord();
            BeanUtils.copyProperties(dataBackFlowRetryMQDTO,dataBackflowRetryFailRecord);
            dataBackflowRetryFailRecord.setReponseData(JSON.toJSONString(tianchengUapResponse));
            dataBackflowRetryFailRecord.setCreateTime(now);
            dataBackflowRetryFailRecordMapper.insertSelective(dataBackflowRetryFailRecord);
        }
    }

    @Transactional(rollbackFor = Exception.class)
    public void dataBackFlowRetryExceptionDataUpdate(DataBackFlowRetryMQDTO dataBackFlowRetryMQDTO,Exception exception){

        Date now = new Date();
        if(dataBackFlowRetryMQDTO.getCallTimes() >= (DATA_BACK_FLOW_RETRY_MAX_CALL_TIMES-1)){
            dataBackflowRetryTaskMapper.deleteByPrimaryKey(dataBackFlowRetryMQDTO.getId());

            DataBackflowRetryFailRecord dataBackflowRetryFailRecord = new DataBackflowRetryFailRecord();
            BeanUtils.copyProperties(dataBackFlowRetryMQDTO,dataBackflowRetryFailRecord);
            dataBackflowRetryFailRecord.setReponseData(exception.getMessage());
            dataBackflowRetryFailRecord.setCreateTime(now);
            dataBackflowRetryFailRecordMapper.insertSelective(dataBackflowRetryFailRecord);
        }else{
            DataBackflowRetryTask dataBackflowRetryTask = new DataBackflowRetryTask();
            dataBackflowRetryTask.setId(dataBackFlowRetryMQDTO.getId());
            dataBackflowRetryTask.setProcessStatus(DataBackFlowRetryStatusEnum.FAIL_PROCESS.getProcessStatus());
            dataBackflowRetryTask.setCallTimes(dataBackFlowRetryMQDTO.getCallTimes()+1);
            dataBackflowRetryTask.setUpdateTime(now);
            dataBackflowRetryTaskMapper.updateByPrimaryKeySelective(dataBackflowRetryTask);
        }
    }

    private DataBackFlowRetryService getSelfProxy() {
        return (DataBackFlowRetryService) AopContext.currentProxy();
    }
}
